1 using System;
2 using System.Collections.Generic;
3 using UnityEngine;
4
5 namespace ProceduralToolkit
6 {
7 public partial class MeshDraft
8 {
9 #region Mesh parts
10
11 public static MeshDraft Triangle(Vector3 vertex0, Vector3 vertex1, Vector3 vertex2)
12 {
13 var normal = Vector3.Cross((vertex1 - vertex0), (vertex2 - vertex0)).normalized;
14 return new MeshDraft
15 {
16 vertices = new List<Vector3>(3) {vertex0, vertex1, vertex2},
17 normals = new List<Vector3>(3) {normal, normal, normal},
18 uv = new List<Vector2>(3) {new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1)},
19 triangles = new List<int>(3) {0, 1, 2},
20 name = "Triangle"
21 };
22 }
23
24 public static MeshDraft Quad(Vector3 origin, Vector3 width, Vector3 length)
25 {
26 var normal = Vector3.Cross(length, width).normalized;
27 return new MeshDraft
28 {
29 vertices = new List<Vector3>(4) {origin, origin + length, origin + length + width, origin + width},
30 normals = new List<Vector3>(4) {normal, normal, normal, normal},
31 uv = new List<Vector2>(4) {new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0)},
32 triangles = new List<int>(6) {0, 1, 2, 0, 2, 3},
33 name = "Quad"
34 };
35 }
36
37 public static MeshDraft Quad(Vector3 vertex0, Vector3 vertex1, Vector3 vertex2, Vector3 vertex3)
38 {
39 var normal = Vector3.Cross((vertex1 - vertex0), (vertex2 - vertex0)).normalized;
40 return new MeshDraft
41 {
42 vertices = new List<Vector3>(4) {vertex0, vertex1, vertex2, vertex3},
43 normals = new List<Vector3>(4) {normal, normal, normal, normal},
44 uv = new List<Vector2>(4) {new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1), new Vector2(1, 0)},
45 triangles = new List<int>(6) {0, 1, 2, 0, 2, 3},
46 name = "Quad"
47 };
48 }
49
50 /// <remarks>
51 /// https://en.wikipedia.org/wiki/Triangle_fan
52 /// </remarks>
53 public static MeshDraft TriangleFan(List<Vector3> vertices)
54 {
55 var draft = new MeshDraft
56 {
57 vertices = vertices,
58 triangles = new List<int>(vertices.Count - 2),
59 normals = new List<Vector3>(vertices.Count),
60 uv = new List<Vector2>(vertices.Count),
61 name = "TriangleFan"
62 };
63 for (int i = 1; i < vertices.Count - 1; i++)
64 {
65 draft.triangles.Add(0);
66 draft.triangles.Add(i);
67 draft.triangles.Add(i + 1);
68 }
69 var normal = Vector3.Cross(vertices[1] - vertices[0], vertices[2] - vertices[0]).normalized;
70 for (int i = 0; i < vertices.Count; i++)
71 {
72 draft.normals.Add(normal);
73 draft.uv.Add(new Vector2((float) i/vertices.Count, (float) i/vertices.Count));
74 }
75 return draft;
76 }
77
78 /// <remarks>
79 /// https://en.wikipedia.org/wiki/Triangle_strip
80 /// </remarks>
81 public static MeshDraft TriangleStrip(List<Vector3> vertices)
82 {
83 var draft = new MeshDraft
84 {
85 vertices = vertices,
86 triangles = new List<int>(vertices.Count - 2),
87 normals = new List<Vector3>(vertices.Count),
88 uv = new List<Vector2>(vertices.Count),
89 name = "TriangleStrip"
90 };
91 for (int i = 0, j = 1, k = 2; i < vertices.Count - 2; i++, j += i%2*2, k += (i + 1)%2*2)
92 {
93 draft.triangles.Add(i);
94 draft.triangles.Add(j);
95 draft.triangles.Add(k);
96 }
97 var normal = Vector3.Cross(vertices[1] - vertices[0], vertices[2] - vertices[0]).normalized;
98 for (int i = 0; i < vertices.Count; i++)
99 {
100 draft.normals.Add(normal);
101 draft.uv.Add(new Vector2((float) i/vertices.Count, (float) i/vertices.Count));
102 }
103 return draft;
104 }
105
106 public static MeshDraft BaselessPyramid(float radius, int segments, float heignt, bool inverted = false)
107 {
108 return BaselessPyramid(Vector3.zero, Vector3.up*heignt*(inverted ? -1 : 1), radius, segments, inverted);
109 }
110
111 public static MeshDraft BaselessPyramid(Vector3 baseCenter, Vector3 apex, float radius, int segments,
112 bool inverted = false)
113 {
114 float segmentAngle = 360f/segments*(inverted ? -1 : 1);
115 float currentAngle = 0;
116
117 var vertices = new Vector3[segments + 1];
118 vertices[0] = apex;
119 for (var i = 1; i <= segments; i++)
120 {
121 vertices[i] = PTUtils.PointOnCircle3XZ(radius, currentAngle) + baseCenter;
122 currentAngle += segmentAngle;
123 }
124
125 var draft = new MeshDraft {name = "BaselessPyramid"};
126 for (var i = 1; i < segments; i++)
127 {
128 draft.Add(Triangle(vertices[0], vertices[i], vertices[i + 1]));
129 }
130 draft.Add(Triangle(vertices[0], vertices[vertices.Length - 1], vertices[1]));
131 return draft;
132 }
133
134 public static MeshDraft BaselessPyramid(Vector3 apex, List<Vector3> ring)
135 {
136 var draft = new MeshDraft {name = "BaselessPyramid"};
137 for (var i = 0; i < ring.Count - 1; i++)
138 {
139 draft.Add(Triangle(apex, ring[i], ring[i + 1]));
140 }
141 draft.Add(Triangle(apex, ring[ring.Count - 1], ring[0]));
142 return draft;
143 }
144
145 public static MeshDraft Band(List<Vector3> lowerRing, List<Vector3> upperRing)
146 {
147 var draft = new MeshDraft {name = "Band"};
148 if (lowerRing.Count < 3 || upperRing.Count < 3)
149 {
150 Debug.LogError("Array sizes must be greater than 2");
151 return draft;
152 }
153 if (lowerRing.Count != upperRing.Count)
154 {
155 Debug.LogError("Array sizes must be equal");
156 return draft;
157 }
158
159 draft.vertices.AddRange(lowerRing);
160 draft.vertices.AddRange(upperRing);
161
162 var lowerNormals = new List<Vector3>();
163 var upperNormals = new List<Vector3>();
164 var lowerUv = new List<Vector2>();
165 var upperUv = new List<Vector2>();
166 int i0, i1, i2, i3;
167 Vector3 v0, v1, v2, v3;
168 for (int i = 0; i < lowerRing.Count - 1; i++)
169 {
170 i0 = i;
171 i1 = i + lowerRing.Count;
172 i2 = i + 1;
173 i3 = i + 1 + lowerRing.Count;
174 v0 = draft.vertices[i0];
175 v1 = draft.vertices[i1];
176 v2 = draft.vertices[i2];
177 v3 = draft.vertices[i3];
178 draft.triangles.AddRange(new[] {i0, i1, i2});
179 draft.triangles.AddRange(new[] {i2, i1, i3});
180
181 lowerNormals.Add(Vector3.Cross(v1 - v0, v2 - v0).normalized);
182 upperNormals.Add(Vector3.Cross(v3 - v1, v0 - v1).normalized);
183
184 var u = (float) i/(lowerRing.Count - 1);
185 lowerUv.Add(new Vector2(u, 0));
186 upperUv.Add(new Vector2(u, 1));
187 }
188
189 i0 = lowerRing.Count - 1;
190 i1 = lowerRing.Count*2 - 1;
191 i2 = 0;
192 i3 = lowerRing.Count;
193 v0 = draft.vertices[i0];
194 v1 = draft.vertices[i1];
195 v2 = draft.vertices[i2];
196 v3 = draft.vertices[i3];
197 draft.triangles.AddRange(new[] {i0, i1, i2});
198 draft.triangles.AddRange(new[] {i2, i1, i3});
199
200 lowerNormals.Add(Vector3.Cross(v1 - v0, v2 - v0).normalized);
201 upperNormals.Add(Vector3.Cross(v3 - v1, v0 - v1).normalized);
202 draft.normals.AddRange(lowerNormals);
203 draft.normals.AddRange(upperNormals);
204
205 lowerUv.Add(new Vector2(1, 0));
206 upperUv.Add(new Vector2(1, 1));
207 draft.uv.AddRange(lowerUv);
208 draft.uv.AddRange(upperUv);
209
210 return draft;
211 }
212
213 public static MeshDraft FlatBand(List<Vector3> lowerRing, List<Vector3> upperRing)
214 {
215 var draft = new MeshDraft {name = "Flat band"};
216 if (lowerRing.Count < 3 || upperRing.Count < 3)
217 {
218 Debug.LogError("Array sizes must be greater than 2");
219 return draft;
220 }
221 if (lowerRing.Count != upperRing.Count)
222 {
223 Debug.LogError("Array sizes must be equal");
224 return draft;
225 }
226
227 Vector3 v0, v1, v2, v3;
228 for (int i = 0; i < lowerRing.Count - 1; i++)
229 {
230 v0 = lowerRing[i];
231 v1 = upperRing[i];
232 v2 = lowerRing[i + 1];
233 v3 = upperRing[i + 1];
234 draft.Add(Triangle(v0, v1, v2));
235 draft.Add(Triangle(v2, v1, v3));
236 }
237
238 v0 = lowerRing[lowerRing.Count - 1];
239 v1 = upperRing[upperRing.Count - 1];
240 v2 = lowerRing[0];
241 v3 = upperRing[0];
242 draft.Add(Triangle(v0, v1, v2));
243 draft.Add(Triangle(v2, v1, v3));
244
245 return draft;
246 }
247
248 #endregion Mesh parts
249
250 #region Platonic solids
251
252 public static MeshDraft Tetrahedron(float radius)
253 {
254 const float tetrahedralAngle = -19.471220333f;
255 const float segmentAngle = 120;
256 float currentAngle = 0;
257
258 var vertices = new List<Vector3>(4) {new Vector3(0, radius, 0)};
259 for (var i = 1; i < 4; i++)
260 {
261 vertices.Add(PTUtils.PointOnSphere(radius, currentAngle, tetrahedralAngle));
262 currentAngle += segmentAngle;
263 }
264 var draft = Triangle(vertices[0], vertices[1], vertices[2]);
265 draft.Add(Triangle(vertices[1], vertices[3], vertices[2]));
266 draft.Add(Triangle(vertices[0], vertices[2], vertices[3]));
267 draft.Add(Triangle(vertices[0], vertices[3], vertices[1]));
268 draft.name = "Tetrahedron";
269 return draft;
270 }
271
272 public static MeshDraft Cube(float side)
273 {
274 var draft = Hexahedron(side, side, side);
275 draft.name = "Cube";
276 return draft;
277 }
278
279 public static MeshDraft Hexahedron(float width, float length, float height)
280 {
281 return Hexahedron(Vector3.right*width, Vector3.forward*length, Vector3.up*height);
282 }
283
284 public static MeshDraft Hexahedron(Vector3 width, Vector3 length, Vector3 height)
285 {
286 Vector3 corner0 = -width/2 - length/2 - height/2;
287 Vector3 corner1 = width/2 + length/2 + height/2;
288
289 var draft = Quad(corner0, length, width);
290 draft.Add(Quad(corner0, width, height));
291 draft.Add(Quad(corner0, height, length));
292 draft.Add(Quad(corner1, -width, -length));
293 draft.Add(Quad(corner1, -height, -width));
294 draft.Add(Quad(corner1, -length, -height));
295 draft.name = "Hexahedron";
296 return draft;
297 }
298
299 public static MeshDraft Octahedron(float radius)
300 {
301 var draft = BiPyramid(radius, 4, radius);
302 draft.name = "Octahedron";
303 return draft;
304 }
305
306 public static MeshDraft Dodecahedron(float radius)
307 {
308 const float magicAngle1 = 52.62263590f;
309 const float magicAngle2 = 10.81231754f;
310 const float segmentAngle = 72;
311 float currentAngle = 0;
312 var lowerCap = new List<Vector3>();
313 var lowerRing = new List<Vector3>();
314 for (var i = 0; i < 5; i++)
315 {
316 lowerCap.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle1));
317 lowerRing.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle2));
318 currentAngle -= segmentAngle;
319 }
320
321 currentAngle = -segmentAngle/2;
322 var upperCap = new List<Vector3>();
323 var upperRing = new List<Vector3>();
324 for (var i = 0; i < 5; i++)
325 {
326 upperCap.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle1));
327 upperRing.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle2));
328 currentAngle -= segmentAngle;
329 }
330
331 var draft = TriangleFan(lowerCap);
332 draft.Add(FlatBand(lowerCap, lowerRing));
333 draft.Add(FlatBand(lowerRing, upperRing));
334 draft.Add(FlatBand(upperRing, upperCap));
335 upperCap.Reverse();
336 draft.Add(TriangleFan(upperCap));
337 draft.name = "Dodecahedron";
338 return draft;
339 }
340
341 public static MeshDraft Icosahedron(float radius)
342 {
343 const float magicAngle = 26.56505f;
344 const float segmentAngle = 72;
345 float currentAngle = 0;
346
347 var upperRing = new List<Vector3>(5);
348 for (var i = 0; i < 5; i++)
349 {
350 upperRing.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle));
351 currentAngle -= segmentAngle;
352 }
353
354 currentAngle = segmentAngle/2;
355 var lowerRing = new List<Vector3>(5);
356 for (var i = 0; i < 5; i++)
357 {
358 lowerRing.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle));
359 currentAngle -= segmentAngle;
360 }
361
362 var draft = BaselessPyramid(new Vector3(0, -radius, 0), lowerRing);
363 draft.Add(FlatBand(lowerRing, upperRing));
364 upperRing.Reverse();
365 draft.Add(BaselessPyramid(new Vector3(0, radius, 0), upperRing));
366 draft.name = "Icosahedron";
367 return draft;
368 }
369
370 #endregion Platonic solids
371
372 public static MeshDraft Plane(float xSize = 1, float zSize = 1, int xSegments = 1, int zSegments = 1)
373 {
374 float xStep = xSize/xSegments;
375 float zStep = zSize/zSegments;
376 var vertexCount = (xSegments + 1)*(zSegments + 1);
377 var draft = new MeshDraft
378 {
379 name = "Plane",
380 vertices = new List<Vector3>(vertexCount),
381 triangles = new List<int>(xSegments*zSegments*6),
382 normals = new List<Vector3>(vertexCount),
383 uv = new List<Vector2>(vertexCount)
384 };
385 for (int z = 0; z <= zSegments; z++)
386 {
387 for (int x = 0; x <= xSegments; x++)
388 {
389 draft.vertices.Add(new Vector3(x*xStep, 0f, z*zStep));
390 draft.normals.Add(Vector3.up);
391 draft.uv.Add(new Vector2((float) x/xSegments, (float) z/zSegments));
392 }
393 }
394
395 int i = 0;
396 for (int z = 0; z < zSegments; z++)
397 {
398 for (int x = 0; x < xSegments; x++)
399 {
400 draft.triangles.Add(i);
401 draft.triangles.Add(i + xSegments + 1);
402 draft.triangles.Add(i + 1);
403 draft.triangles.Add(i + 1);
404 draft.triangles.Add(i + xSegments + 1);
405 draft.triangles.Add(i + xSegments + 2);
406 i++;
407 }
408 i++;
409 }
410 return draft;
411 }
412
413 public static MeshDraft Pyramid(float radius, int segments, float heignt, bool inverted = false)
414 {
415 var draft = BaselessPyramid(radius, segments, heignt, inverted);
416 var vertices = new List<Vector3>(segments);
417 for (int i = draft.vertices.Count - 2; i >= 0; i -= 3)
418 {
419 vertices.Add(draft.vertices[i]);
420 }
421 draft.Add(TriangleFan(vertices));
422 draft.name = "Pyramid";
423 return draft;
424 }
425
426 public static MeshDraft BiPyramid(float radius, int segments, float heignt)
427 {
428 var draft = BaselessPyramid(radius, segments, heignt);
429 draft.Add(BaselessPyramid(radius, segments, heignt, true));
430 return draft;
431 }
432
433 public static MeshDraft Prism(float radius, int segments, float heignt)
434 {
435 float segmentAngle = 360f/segments;
436 float currentAngle = 0;
437
438 var lowerRing = new List<Vector3>(segments);
439 var upperRing = new List<Vector3>(segments);
440 for (var i = 0; i < segments; i++)
441 {
442 var point = PTUtils.PointOnCircle3XZ(radius, currentAngle);
443 lowerRing.Add(point - Vector3.up*heignt/2);
444 upperRing.Add(point + Vector3.up*heignt/2);
445 currentAngle -= segmentAngle;
446 }
447
448 var draft = TriangleFan(lowerRing);
449 draft.Add(FlatBand(lowerRing, upperRing));
450 upperRing.Reverse();
451 draft.Add(TriangleFan(upperRing));
452 draft.name = "Prism";
453 return draft;
454 }
455
456 public static MeshDraft Cylinder(float radius, int segments, float heignt)
457 {
458 float segmentAngle = 360f/segments;
459 float currentAngle = 0;
460
461 var lowerRing = new List<Vector3>(segments);
462 var upperRing = new List<Vector3>(segments);
463 for (var i = 0; i < segments; i++)
464 {
465 var point = PTUtils.PointOnCircle3XZ(radius, currentAngle);
466 lowerRing.Add(point - Vector3.up*heignt/2);
467 upperRing.Add(point + Vector3.up*heignt/2);
468 currentAngle -= segmentAngle;
469 }
470
471 var draft = TriangleFan(lowerRing);
472 draft.Add(Band(lowerRing, upperRing));
473 upperRing.Reverse();
474 draft.Add(TriangleFan(upperRing));
475 draft.name = "Cylinder";
476 return draft;
477 }
478
479 public static MeshDraft FlatSphere(float radius, int horizontalSegments, int verticalSegments)
480 {
481 var draft = FlatSpheroid(radius, radius, horizontalSegments, verticalSegments);
482 draft.name = "Flat sphere";
483 return draft;
484 }
485
486 public static MeshDraft FlatSpheroid(float radius, float height, int horizontalSegments, int verticalSegments)
487 {
488 var draft = FlatRevolutionSurface(PTUtils.PointOnSpheroid, radius, height, horizontalSegments,
489 verticalSegments);
490 draft.name = "Flat spheroid";
491 return draft;
492 }
493
494 public static MeshDraft FlatTeardrop(float radius, float height, int horizontalSegments, int verticalSegments)
495 {
496 var draft = FlatRevolutionSurface(PTUtils.PointOnTeardrop, radius, height, horizontalSegments,
497 verticalSegments);
498 draft.name = "Flat teardrop";
499 return draft;
500 }
501
502 public static MeshDraft FlatRevolutionSurface(
503 Func<float, float, float, float, Vector3> surfaceFunction,
504 float radius,
505 float height,
506 int horizontalSegments,
507 int verticalSegments)
508 {
509 float horizontalSegmentAngle = 360f/horizontalSegments;
510 float verticalSegmentAngle = 180f/verticalSegments;
511 float currentVerticalAngle = -90;
512
513 var rings = new List<List<Vector3>>(verticalSegments);
514 for (int i = 0; i <= verticalSegments; i++)
515 {
516 float currentHorizontalAngle = 0f;
517 var ring = new List<Vector3>(horizontalSegments);
518 for (int j = 0; j < horizontalSegments; j++)
519 {
520 ring.Add(surfaceFunction(radius, height, currentHorizontalAngle, currentVerticalAngle));
521 currentHorizontalAngle -= horizontalSegmentAngle;
522 }
523 rings.Add(ring);
524 currentVerticalAngle += verticalSegmentAngle;
525 }
526
527 var draft = new MeshDraft {name = "Flat revolution surface"};
528 for (int i = 0; i < rings.Count - 1; i++)
529 {
530 draft.Add(FlatBand(rings[i], rings[i + 1]));
531 }
532 return draft;
533 }
534
535 public static MeshDraft Sphere(float radius, int horizontalSegments, int verticalSegments)
536 {
537 var draft = Spheroid(radius, radius, horizontalSegments, verticalSegments);
538 draft.name = "Sphere";
539 return draft;
540 }
541
542 public static MeshDraft Spheroid(float radius, float height, int horizontalSegments, int verticalSegments)
543 {
544 var draft = RevolutionSurface(PTUtils.PointOnSpheroid, radius, height, horizontalSegments, verticalSegments);
545 draft.name = "Spheroid";
546 return draft;
547 }
548
549 public static MeshDraft Teardrop(float radius, float height, int horizontalSegments, int verticalSegments)
550 {
551 var draft = RevolutionSurface(PTUtils.PointOnTeardrop, radius, height, horizontalSegments,
552 verticalSegments);
553 draft.name = "Teardrop";
554 return draft;
555 }
556
557 public static MeshDraft RevolutionSurface(
558 Func<float, float, float, float, Vector3> surfaceFunction,
559 float radius,
560 float height,
561 int horizontalSegments,
562 int verticalSegments)
563 {
564 var draft = new MeshDraft {name = "Revolution surface"};
565
566 float horizontalSegmentAngle = 360f/horizontalSegments;
567 float verticalSegmentAngle = 180f/verticalSegments;
568 float currentVerticalAngle = -90;
569
570 for (int ring = 0; ring <= verticalSegments; ring++)
571 {
572 float currentHorizontalAngle = 0f;
573 for (int i = 0; i < horizontalSegments; i++)
574 {
575 Vector3 point = surfaceFunction(radius, height, currentHorizontalAngle, currentVerticalAngle);
576 draft.vertices.Add(point);
577 draft.normals.Add(point.normalized);
578 draft.uv.Add(new Vector2((float) i/horizontalSegments, (float) ring/verticalSegments));
579 currentHorizontalAngle -= horizontalSegmentAngle;
580 }
581 currentVerticalAngle += verticalSegmentAngle;
582 }
583
584 for (int ring = 0; ring < verticalSegments; ring++)
585 {
586 int i0, i1, i2, i3;
587 for (int i = 0; i < horizontalSegments - 1; i++)
588 {
589 i0 = ring*horizontalSegments + i;
590 i1 = (ring + 1)*horizontalSegments + i;
591 i2 = ring*horizontalSegments + i + 1;
592 i3 = (ring + 1)*horizontalSegments + i + 1;
593
594 draft.triangles.Add(i0);
595 draft.triangles.Add(i1);
596 draft.triangles.Add(i2);
597
598 draft.triangles.Add(i2);
599 draft.triangles.Add(i1);
600 draft.triangles.Add(i3);
601 }
602
603 i0 = (ring + 1)*horizontalSegments - 1;
604 i1 = (ring + 2)*horizontalSegments - 1;
605 i2 = ring*horizontalSegments;
606 i3 = (ring + 1)*horizontalSegments;
607
608 draft.triangles.Add(i0);
609 draft.triangles.Add(i1);
610 draft.triangles.Add(i2);
611
612 draft.triangles.Add(i2);
613 draft.triangles.Add(i1);
614 draft.triangles.Add(i3);
615 }
616 return draft;
617 }
618
619 /// <summary>
620 /// Constructs partial box with specified faces
621 /// </summary>
622 public static MeshDraft PartialBox(Vector3 width, Vector3 length, Vector3 height, Directions parts)
623 {
624 Vector3 corner0 = -width/2 - length/2 - height/2;
625 Vector3 corner1 = width/2 + length/2 + height/2;
626
627 var draft = new MeshDraft {name = "Hexahedron"};
628 if ((parts & Directions.Left) == Directions.Left)
629 {
630 draft.Add(Quad(corner0, height, length));
631 }
632 if ((parts & Directions.Right) == Directions.Right)
633 {
634 draft.Add(Quad(corner1, -length, -height));
635 }
636 if ((parts & Directions.Down) == Directions.Down)
637 {
638 draft.Add(Quad(corner0, length, width));
639 }
640 if ((parts & Directions.Up) == Directions.Up)
641 {
642 draft.Add(Quad(corner1, -width, -length));
643 }
644 if ((parts & Directions.Back) == Directions.Back)
645 {
646 draft.Add(Quad(corner0, width, height));
647 }
648 if ((parts & Directions.Forward) == Directions.Forward)
649 {
650 draft.Add(Quad(corner1, -height, -width));
651 }
652 return draft;
653 }
654 }
655 }